﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using Utils.SoftReferences;
//using Microsoft.Xna.Framework.Graphics;

namespace GameObjects
{
    [Serializable]
    [XmlInclude(typeof(AirborneUnit))]
    [XmlInclude(typeof(WaterborneUnit))]
    public class Unit : BaseSoftRefTarget
    {
        public string Name;

        [XmlIgnore]
        public TerrainType PreferredTerrain;
        public int? PreferredTerrainId;

        public MetaData.MoveType MoveType;
        
        [XmlElement("MoveLimit")]
        public int BaseMoveLimit;

        

        [XmlIgnore]
        public Faction OriginalOwner;
        [XmlIgnore]
        public Faction Owner
        {
            get { return _owner; }   
            set 
            { 
                _owner = value;
                OwnerId = value.Id;
            }
        }
        private Faction _owner;

        [XmlElement("Quality")]
        public float BaseQuality;

        
        public float Morale;
        public float BaseMorale;

        
        [XmlIgnore]
        public TerrainType ConcealmentTerrain;

        public int? ConcealmentTerrainId;

        
        public int Vision;
        public bool HinderedByColdWeather;

        //public Unit(SoftRefSet softRefSet, string name, Hex cell, MetaData.MoveType moveType, TerrainType terrainType, Faction player, int moveLimit, float quality, int quantity, float baseMorale, float initialMorale, int vision, TerrainType concealmentTerrain, bool hinderedByColdWeather, IEnumerable<TerrainType> terrainTypes)
        //    : base(softRefSet)
        //{
        //    Name = name;
        //    OriginalOwner = Owner = player;
        //    Vision = vision;
        //}

        [XmlIgnore]
        public List<Move> MoveList = new List<Move>();
        [XmlIgnore]
        public List<UnitTerrainCost> MovementTable { get; set; }

        public int OriginalOwnerId;
        public int OwnerId;
        public int HexId;

        [XmlIgnore]
        public Hex Location
        {
            get { return _location; }
            set
            {
                _location = value;
                HexId = value.Id;

                var reveiledHexes = new List<Hex> { value };
                if (Vision > 0)
                {
                    reveiledHexes.AddRange(value.AdjacentCells);
                    if (Vision > 1)
                    {
                        foreach (var extra in value.AdjacentCells)
                            if (extra.MovementCost != MetaData.MovementCost.Impassable)
                                reveiledHexes.AddRange(extra.AdjacentCells);
                    }
                }

                foreach (var reveiledHex in reveiledHexes)
                    if (!Owner.DiscoveredHexes.Contains(reveiledHex))
                        Owner.DiscoveredHexes.Add(reveiledHex);
            }
        }
        private Hex _location;

        [XmlIgnore]
        public float StructureStrengthModifier
        {
            get
            {
                return Location.Structure != null && Location.Structure.Owner == Owner ? Location.Structure.StrengthBonus : 1;
            }
        }

        [XmlIgnore]
        public string Color
        {
            get { return Owner.UnitColour; }
        }

        public int Casulties
        {
            get;
            set;
        }
        
        public int Quantity
        {
            get
            {
                return quantity - Casulties;
            }
        }
        private int quantity;

        public float Quality
        {
            get 
            {
                if (MoraleStatus == MetaData.MoraleStatus.Fanatical)
                    return BaseQuality * 1.3f;
                if (MoraleStatus == MetaData.MoraleStatus.Inspired)
                    return BaseQuality * 1.1f;
                //if (MoraleStatus == MetaData.MoraleStatus.Demoralised)
                //    return _quality * .95f;
                //if (MoraleStatus == MetaData.MoraleStatus.Rebellious)
                //    return _quality * .9f;
                return BaseQuality;
            }
        }

        [XmlIgnore]
        public bool Alive
        {
            get
            {
                return Quantity > 0;
            }
        }

        [XmlIgnore]
        public MetaData.MoraleStatus MoraleStatus
        {
            get
            {
                if (Morale > BaseMorale)
                    return MetaData.MoraleStatus.Inspired;
                if (Morale > BaseMorale * .75)
                    return MetaData.MoraleStatus.Stable;
                if (Morale > BaseMorale * .5)
                    return MetaData.MoraleStatus.Fatigued;
                if (Morale > BaseMorale * .25)
                    return MetaData.MoraleStatus.Demoralised;
                return MetaData.MoraleStatus.Rebellious;
            }
        }

        public Unit()
        {
        }

        public Unit(SoftRefSet softRefSet, string name, Hex cell, MetaData.MoveType moveType, TerrainType terrainType, Faction player, int moveLimit, float quality, int quantity, float baseMorale, float initialMorale, int vision, TerrainType concealmentTerrain, bool hinderedByColdWeather, IEnumerable<TerrainType> terrainTypes)
            : base(softRefSet)
        {
            Name = name;
            OriginalOwner = Owner = player;
            OriginalOwnerId = player.Id;

            Vision = vision;

            Location = cell;
            PreferredTerrain = terrainType;
            PreferredTerrainId = PreferredTerrain == null ? null : (int?)PreferredTerrain.Id;
            MoveType = moveType;
            BaseMoveLimit = moveLimit;
            BaseQuality = quality;
            this.quantity = quantity;
            BaseMorale = baseMorale;
            Morale = initialMorale;
            ConcealmentTerrain = concealmentTerrain;
            ConcealmentTerrainId = ConcealmentTerrain == null ? null : (int?)ConcealmentTerrain.Id;
            HinderedByColdWeather = hinderedByColdWeather;
            SetMovementTable(terrainTypes);
        }

        

        public bool MoveAlreadyAdded(Move move)
        {
            return  MoveList.Any(o => o.PreviousMove.Destination == move.PreviousMove.Destination && o.Destination == move.Destination);
        }


        public void ApplyMoraleEffects()
        {
            if (Quantity <= 0) return;

            switch (MoraleStatus)
            {
                case MetaData.MoraleStatus.Fatigued:
                    Casulties = (int)((float)Quantity * .98f);
                    break;
                case MetaData.MoraleStatus.Demoralised:
                    Casulties = (int)((float)Quantity * .95f);
                    break;
                case MetaData.MoraleStatus.Rebellious:
                    Casulties = (int)((float)Quantity * .9f);
                    break;
            }
            
        }

        public void Rebel(Faction faction)
        {
            Owner = faction;
            if (Location.Structure != null)
                Location.Structure.Owner = faction;
            Morale = BaseMorale;
        }

        private void SetMovementTable(IEnumerable<TerrainType> terrainTypes)
        {
            MovementTable = new List<UnitTerrainCost>();
            foreach (var terrainType in terrainTypes)
            {
                var unitTerrainCost = new UnitTerrainCost(terrainType)
                                          {
                                              MovementCost = terrainType.MovementType == MoveType
                                                      ? terrainType.MovementCost
                                                      : MetaData.MovementCost.Impassable
                                          };

                // Airborne units can fly over anything for a cost of one, except impassable terrain
                if (MoveType == MetaData.MoveType.Airborne)
                {
                    unitTerrainCost.MovementCost = terrainType.MovementCost == MetaData.MovementCost.Impassable ?
                        MetaData.MovementCost.OneToFlyOver :
                        MetaData.MovementCost.One;

                    if (terrainType.MovementType != MetaData.MoveType.Land)
                        unitTerrainCost.MovementCost = MetaData.MovementCost.OneToFlyOver;
                }

                // Water units can move into any land terrain for a cost of all, except impassable terrain
                if (MoveType == MetaData.MoveType.Water)
                {
                    if (terrainType.MovementType == MetaData.MoveType.Land)
                        unitTerrainCost.MovementCost = terrainType.MovementCost == MetaData.MovementCost.Impassable ?
                        MetaData.MovementCost.Impassable :
                        MetaData.MovementCost.AllOnCoast;
                }

                // Units can move over their preferred terrain for only one
                if (PreferredTerrain != null && PreferredTerrain == terrainType)
                    unitTerrainCost.MovementCost = MetaData.MovementCost.One;

                MovementTable.Add(unitTerrainCost);
            }
        }
    }
}
